package com.smartmail.receiving;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.os.IBinder;
import android.util.Log;

import com.smartmail.SmartMailPreferences;
import com.smartmail.accounts.Accounts;
import com.smartmail.accounts.MailAccount;
import com.smartmail.receiving.imap.IMAPRetrievalThread;
import com.smartmail.receiving.pop3.POP3RetrievalThread;

/**
 * Android's Service class responsible for listening mailboxes for new messages, and handling them.
 * 
 * @author Tomek
 *
 */
public class RetrievalService extends Service {

	
	/**
	 * Stop execution of all 'retrieval threads' in application
	 */
	public synchronized void stopAllThreads() {

		for (Map.Entry<Integer, AbstractRetrievalThread> entry : retrievalThreads
				.entrySet()) {
			AbstractRetrievalThread thread = entry.getValue();
			thread.stopThread();
		}

	}

	/**
	 * Resume execution of all 'retrieval threads' in application
	 */
	public synchronized void  resumeAllThreads() {

		for (Map.Entry<Integer, AbstractRetrievalThread> entry : retrievalThreads
				.entrySet()) {
			AbstractRetrievalThread thread = entry.getValue();
			thread.resumeThread();
		}

	}
	
	/**
	 * Stop execution of 'retrieval thread' for that account, and 
	 * delete this 'retrieval thread' from application.
	 * 
	 * @param mailAccount 
	 * @return - true on success, otherwise false.
	 */
	public static boolean deleteRetrievalThread(MailAccount mailAccount){
		
		AbstractRetrievalThread thread = retrievalThreads.get(mailAccount.getAccountID());
		if( thread == null )
			return true;
		
		retrievalThreads.remove(mailAccount.getAccountID());
		boolean result = thread.killThread();
		return result;

	}
	
	/**
	 * @return - Map containing AbstractRetrievalThreads (value) for AccountID (key)
	 */
	public static Map<Integer, AbstractRetrievalThread> getRetrievalThreads(){
		
		return retrievalThreads;
	}
	
	/**
	 * @param account - MailAccount account
	 * @return - AbstractRetrievalThread for this MailAccount
	 */
	public static AbstractRetrievalThread getRetrievalThread(MailAccount account){
	
		return getRetrievalThread(account.getAccountID());
	}
	
	public static AbstractRetrievalThread getRetrievalThread(Integer accountID){
		
		return retrievalThreads.get(accountID);
	}

	/**
	 * Initialize AbstractRetrievalThread for account.
	 * 
	 * @param account -  Mail Account which 'retrieval thread' will be initialized
	 */
	private void initializeThread(MailAccount account) {

		AbstractRetrievalThread t = retrievalThreads.get(account.getAccountID());
		
		if (t != null && t.isRunning())
			return;
		
		AbstractRetrievalThread thread = null;

		// Check which protocol this account supports
		switch (account.getIncomingMailServerProtocolType()) {

			case IMAP: {
	
				thread = new IMAPRetrievalThread(account, Receiving.getDBManager(account), this);
				break;
			}
	
			case POP3: {
				
				thread = new POP3RetrievalThread(account,Receiving.getDBManager(account), this);
				break;
			}
		}
		
		if(thread != null){
			
			thread.restartThread();
			retrievalThreads.put(account.getAccountID(), thread);
		}

	}

	/**
	 * Initialize 'retrieval threads' for all mail accounts in the list
	 * @param accounts - list of Mail Accounts whose 'retrieval threads' will be
	 * initialized.
	 */
	private void initializeThreads(List<MailAccount> accounts) {

		for (MailAccount account : accounts) {

			initializeThread(account);
		}
	}

	public void onCreate() {

		super.onCreate();
		
		Log.d(_logTag, "RetrievalService : onCreate");

		retrievalThreads = new HashMap<Integer, AbstractRetrievalThread>();
		initializeThreads(Accounts.getAccountList());

		_connectivityBroadcastReceiver = new ConnectivityBroadcastReceiver(this);
		_connectivityIntentFilter = new IntentFilter(
				ConnectivityManager.CONNECTIVITY_ACTION);
		registerReceiver(_connectivityBroadcastReceiver,
				_connectivityIntentFilter);

		_newAccountReceiver = new NewAccountAddedBroadcastReceiver();
		_newAccountAddedIntentFilter = new IntentFilter(
				Accounts.NEW_ACCOUNT_ADDED_ACTION);
		registerReceiver(_newAccountReceiver, _newAccountAddedIntentFilter);
		
		_globalPropertyReceiver = new GlobalPropertyChangedBroadcastReceiver();
		_globalPropertyIntentFilter = new IntentFilter(SmartMailPreferences.GLOBAL_PROPERTY_CHANGED_INTENT);
		registerReceiver(_globalPropertyReceiver, _globalPropertyIntentFilter);

	}
	
	public void onDestroy() {

		super.onDestroy();

		for (Map.Entry<Integer, AbstractRetrievalThread> entry : retrievalThreads
				.entrySet()) {
			AbstractRetrievalThread thread = entry.getValue();

			thread.stopThread();
		}

		unregisterReceiver(_connectivityBroadcastReceiver);
		unregisterReceiver(_newAccountReceiver);
		unregisterReceiver(_globalPropertyReceiver);
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	
	public int onStartCommand(Intent intent, int flags, int startId) {

		super.onStartCommand(intent, flags, startId);

		for (Map.Entry<Integer, AbstractRetrievalThread> entry : retrievalThreads
				.entrySet()) {
			AbstractRetrievalThread thread = entry.getValue();

			if (!thread.isRunning())
				thread.resumeThread();
		}

		return START_STICKY;
	}
	


	/**
	 * Broadcast receiver which received intent about creation of
	 *  new account, it initializes receiving thread for that account
	 * 
	 * @author Tomek 
	 */
	private class NewAccountAddedBroadcastReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {

			if (intent.getAction().equals(Accounts.NEW_ACCOUNT_ADDED_ACTION)) {

				int accountID = intent.getIntExtra(
						Accounts.NEW_ACCOUNT_ADDED_ID, -1);
				if (accountID == -1)
					return;

				MailAccount account = Accounts.getAccount(accountID);
				initializeThread(account);

				Log.d(_logTag, "RetrievalService : new account  added : "
						+ account.getMailAddress());
			}

		}

	}
	
	/**
	 * Broadcast receiver which receives intent about global property changes.
	 * When global property changes restart all 'retrieval threads'.
	 * 
	 * @author Tomek
	 *
	 */
	private class GlobalPropertyChangedBroadcastReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
			
			for (Map.Entry<Integer, AbstractRetrievalThread> entry : retrievalThreads.entrySet()) {
				
				AbstractRetrievalThread thread = entry.getValue();
				if( thread.isRunning() )
					thread.restartThread();
			}
			
		}
		
	}
	
	public static final String _logTag = "com.smartmail.receiving";

	/**
	 * Broadcast receiver to handle information about new account creation.
	 */
	private NewAccountAddedBroadcastReceiver _newAccountReceiver;
	private IntentFilter _newAccountAddedIntentFilter;
	
	/**
	 * Broadcast receiver to handle information about global properties change.
	 */
	private GlobalPropertyChangedBroadcastReceiver _globalPropertyReceiver;
	private IntentFilter _globalPropertyIntentFilter;
	
	/**
	 * Broadcast receiver which receives information about internet connection
	 * status in case of connection lost - all threads are stopped
	 */
	private ConnectivityBroadcastReceiver _connectivityBroadcastReceiver;
	private IntentFilter _connectivityIntentFilter;

	/**
	 * Map of 'AbstractRetrievalThreds' threads for every listening mail account 
	 * Key - 	accountID 
	 * Value - 	AbstractRetrievalThread
	 */
	private static Map<Integer, AbstractRetrievalThread> retrievalThreads;

}
